﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.ComponentModel;

namespace AuthenticationToken
{
    /// <summary>
    /// Limitation:
    /// 1. Only accepts primitive data types(refer http://msdn.microsoft.com/en-us/library/system.type.isprimitive.aspx)
    ///    with nullable and DateTime and GUID type
    /// 2. While parsing, value of nullable is not found in token then it assigns the default value of that type.
    ///    e.g. for int? type its values become 0 (default) if its corresponding value does not found in the token
    /// </summary>
    public class AuthenticationToken
    {
        public static string Generate<T>(T obj, string key)
        {
            Type type = obj.GetType();
            StringBuilder tokenData = new StringBuilder();
            foreach (PropertyInfo property in type.GetProperties())
            {
                tokenData.Append(property.Name);
                tokenData.Append(Separators.NameValueSeparator);
                tokenData.Append(property.GetValue(obj, null));
                tokenData.Append(Separators.PairSeparator);
            }
            return Cryptography.Encrypt(tokenData.ToString(), key);
        }
        public static void Parse<T>(ref T obj, string token, string key)
        {

            token = Cryptography.Decrypt(token, key);
            foreach (PropertyInfo property in obj.GetType().GetProperties())
            {
                string propertyValue = GetPropertyValueFromToken(token, property.Name);
                object value = propertyValue.Length==0?null:propertyValue;
                Type type = property.PropertyType;
                bool isNullableType = type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>));
                if (type.IsPrimitive || type == typeof(Guid) || type == typeof(DateTime) || isNullableType)
                {
                    if (isNullableType)
                    {
                        NullableConverter nullableConverter = new NullableConverter(type);
                        type = nullableConverter.UnderlyingType;
                        value = value == null ? (object)Activator.CreateInstance(type) : value;
                    }
                    if (type == typeof(Guid))
                        property.SetValue(obj, new Guid(value.ToString()), null);
                    else
                        property.SetValue(obj, Convert.ChangeType(value, type), null);
                }
            }
        }
        private static string GetPropertyValueFromToken(string token, string propertyName)
        {
            //Property1»1111«Property2»2222«Property3»3333«
            int valueStart = token.IndexOf(propertyName + Separators.NameValueSeparator.ToString());
            valueStart += propertyName.Length + 1;
            int valueEnd = token.IndexOf(Separators.PairSeparator.ToString(), valueStart);
            string value = token.Substring(valueStart, valueEnd - valueStart);
            return value;
        }
    }

}
